﻿namespace Baddies
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Baddies.Events;
    using Baddies.Nodes;
    using Baddies.Utils;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;    

    /// <summary>
    /// Camera class that keeps track of what should be drawn on screen.
    /// <remarks>
    /// The camera works as follows:
    /// You add the camera to a node, usually a Scene. The node then has the
    /// camera position as it's own. 
    /// Each update cycle the camera updates the position of the
    /// parent node, and then commands a Cull to take place on the parent node.
    /// During a cull, every node in the subtree of root the parent node gets
    /// checked to see if they are in the camera. Each node has it's own Cull
    /// function as different nodes will have different cull conditions.
    /// Later, during the draw pass, each node is checked to see if they have been 
    /// culled, and if so they are not drawn, and the cull flag gets reset.
    /// </remarks>
    /// </summary>
    public class Camera : Node
    {
        /// <summary>
        /// Widht of the frustrum.
        /// </summary>
        private int width;

        /// <summary>
        /// Height of the frustrum.
        /// </summary>
        private int height;

        /// <summary>
        /// Texture to draw the edge of the camera.
        /// </summary>
        private Texture2D borderTex;

        /// <summary>
        /// Indicatres whether we have to draw the border.
        /// </summary>
        private bool drawBorder;

        /// <summary>
        /// Initializes a new instance of the Camera class.
        /// </summary>
        public Camera()
        {
            this.width = 10;
            this.height = 10;
            this.ParentRelativePos = false;

            // Create border texture.
            this.CreateBorderTexture();
        }

        /// <summary>
        /// Initializes a new instance of the Camera class.
        /// </summary>
        /// <param name="width">Frustrum width.</param>
        /// <param name="height">Frustrum height.</param>
        public Camera(int width, int height)
        {
            this.width = width;
            this.height = height;
            this.ParentRelativePos = false;

            // Create border texture.
            this.CreateBorderTexture();
        }

        /// <summary>
        /// Generates the texture for the camera border and stores it in borderTex.
        /// </summary>
        private void CreateBorderTexture()
        {
            GetGameGfxDeviceArg args = new GetGameGfxDeviceArg();
            EventManager.GetEV.Trigger("GetGameGfxDevice", this, args);
            this.borderTex = new Texture2D(args.GraphicsDevice, 1, 1);
            this.borderTex.SetData(new[] { Color.Green });
        }

        /// <summary>
        /// Gets or sets the width of the camera.
        /// </summary>
        /// <value>Width of the camera.</value>
        public int W
        {
            get { return this.width; }
            set { this.width = value; }
        }

        /// <summary>
        /// Gets or sets the height of the camera.
        /// </summary>
        /// <value>Height of the camera.</value>
        public int H
        {
            get { return this.height; }
            set { this.height = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to display the camera border.
        /// </summary>
        /// <value>True to display the camera border, false otherwise.</value>
        public bool DisplayBorder
        {
            set { this.drawBorder = value; }
            get { return this.drawBorder; }
        }

        /// <summary>
        /// Moves the camera to a new position instantlly.
        /// </summary>
        /// <param name="newPos">New position to move to.</param>
        public void MoveTo(Vector3 newPos)
        {
            this.Position = newPos;
        }

        /// <summary>
        /// Translates the camera in a direction, a given size step
        /// Will be used for a translation funcion to move the camera seamlessly.
        /// </summary>
        /// <param name="dir">Direction to move.</param>
        /// <param name="step">Step size to move.</param>
        public void Move(Util.Dir dir, int step)
        {
            switch (dir)
            {
                case Util.Dir.Down:
                    this.OffsetPosition(0, step, 0);
                    break;
                case Util.Dir.Up:
                    this.OffsetPosition(0, -step, 0);
                    break;
                case Util.Dir.Right:
                    this.OffsetPosition(step, 0, 0);                    
                    break;
                case Util.Dir.Left:
                    this.OffsetPosition(-step, 0, 0);
                    break;
            }
        }        

        /// <summary>
        /// Updates the camera.
        /// </summary>
        public override void Update()
        {
            base.Update();

            // Set the camera pos as the pos of the parent node.
            this.Parent.Position = new Vector3(-this.Position.X, -this.Position.Y, -this.Position.Z);

            // Make a cull pass
            this.Parent.NodeCull(this);
        }

        /// <summary>
        /// Draws the camera border if required.
        /// </summary>
        /// <param name="spritebatch">Spritebatch to draw in.</param>
        public override void Draw(SpriteBatch spritebatch)
        {
            base.Draw(spritebatch);
            if (this.drawBorder)
            {
                // Draw the 4 lines
                spritebatch.Draw(this.borderTex, new Rectangle(0, 0, this.width, 1), Color.Red);
                spritebatch.Draw(this.borderTex, new Rectangle(0, 0, 1, this.height), Color.Red);
                spritebatch.Draw(this.borderTex, new Rectangle(0, this.height, this.width, 1), Color.Red);
                spritebatch.Draw(this.borderTex, new Rectangle(this.width, 0, 1, this.height), Color.Red);
            }
        }
    }
}
